@file:Suppress("unused", "NOTHING_TO_INLINE")

package com.xh.baselibrary.ex

import org.json.JSONArray
import org.json.JSONObject

/**
 * Note:
 *
 * @author sens 2019/10/31 13:51
 */
inline val JSONArray.size: Int get() = this.length()

inline val JSONArray.lastIndex: Int get() = this.size - 1

inline val JSONArray.indices: IntRange get() = 0 until this.size

inline fun JSONArray.isEmpty(): Boolean = this.size == 0

inline fun JSONArray.isNotEmpty(): Boolean = this.size > 0

inline fun JSONArray?.isNullOrEmpty(): Boolean = this == null || this.size == 0

inline fun <reified T> JSONArray.first(): T = this.read(0)

inline fun <reified T> JSONArray.firstOrNull(): T? = if (this.isEmpty()) null else this.read(0)

inline fun <reified T> JSONArray.first(predicate: (T) -> Boolean): T {
    for (i in this.indices) {
        val t = this.read<T>(i)
        if (predicate(t)) {
            return t
        }
    }

    throw NoSuchElementException("JSONArray contains no element matching the predicate.")
}

inline fun <reified T> JSONArray.firstOrNull(predicate: (T) -> Boolean): T? {
    for (i in this.indices) {
        val t = this.read<T>(i)
        if (predicate(t)) {
            return t
        }
    }

    return null
}

inline fun <reified T, V> JSONArray.mapFirst(predicate: (T) -> V?): V {
    for (i in this.indices) {
        val v = predicate(this.read(i))
        if (v != null) {
            return v
        }
    }

    throw NoSuchElementException("JSONArray contains no element matching the predicate.")
}

inline fun <reified T, V> JSONArray.mapFirstOrNull(predicate: (T) -> V?): V? {
    for (i in this.indices) {
        val v = predicate(this.read(i))
        if (v != null) {
            return v
        }
    }

    return null
}

inline fun <reified T> JSONArray.last(): T = this.getValue(-1)


inline fun <reified T> JSONArray.last(predicate: (T) -> Boolean): T {
    for (i in this.indices.reversed()) {
        val t = this.read<T>(i)
        if (predicate(t)) {
            return t
        }
    }

    throw NoSuchElementException("JSONArray contains no element matching the predicate.")
}

inline fun <reified T> JSONArray.lastOrNull(): T? = if (this.isEmpty()) null else this.read(this.size - 1)

inline fun <reified T> JSONArray.lastOrNull(predicate: (T) -> Boolean): T? {
    for (i in this.indices.reversed()) {
        val t = this.read<T>(i)
        if (predicate(t)) {
            return t
        }
    }

    return null
}

inline fun <reified T, V> JSONArray.mapLast(predicate: (T) -> V?): V {
    for (i in this.indices.reversed()) {
        val v = predicate(this.read(i))
        if (v != null) {
            return v
        }
    }

    throw NoSuchElementException("JSONArray contains no element matching the predicate.")
}

inline fun <reified T, V> JSONArray.mapLastOrNull(predicate: (T) -> V?): V? {
    for (i in this.indices.reversed()) {
        val v = predicate(this.read(i))
        if (v != null) {
            return v
        }
    }

    return null
}

inline fun <reified T> JSONArray.forEach(action: (T) -> Unit) {
    repeat(this.size) { action(this.read(it)) }
}

inline fun <reified T> JSONArray.forEachIndexed(action: (Int, T) -> Unit) {
    repeat(this.size) { action(it, this.read(it)) }
}

inline fun <reified T, R> JSONArray.map(action: (T) -> R): MutableList<R> {
    return map(mutableListOf(), action)
}

inline fun <reified T, R> JSONArray.map(list: MutableList<R>, action: (T) -> R): MutableList<R> {
    repeat(this.size) { list.add(action(this.read(it))) }
    return list
}

inline fun <reified T, R> JSONArray.mapIndexed(action: (Int, T) -> R): MutableList<R> {
    return mapIndexed(mutableListOf(), action)
}

inline fun <reified T, R> JSONArray.mapIndexed(list: MutableList<R>, action: (Int, T) -> R): MutableList<R> {
    repeat(this.size) { list.add(action(it, this.read(it))) }
    return list
}

/**
 * 连续读取json结构体
 *
 * @param keys 要读取的每层的key，这里的key可以是[String]或[Int]两种类型。
 * [String]对应读取[JSONObject]里的数据，[Int]对应读取[JSONArray]里的数据，-1表示最后一条数据。
 */
inline fun <reified T> JSONArray.getValue(vararg keys: Any): T {
    if (keys.size == 1) {
        val index = keys[0] as Int
        return if (index == -1) this.read(this.lastIndex) else this.read(index)
    }

    // 中间层读取
    var json: Any = this
    for (i in 0 .. keys.size - 2) {
        val key = keys[i]

        // 根据key类型决定当前的json是JSONObject还是JSONArray
        // 根据下一个key的类型决定读取的值是JSONObject还是JSONArray
        json = if (key is String) {
            json as JSONObject
            if (keys[i + 1] is String) {
                json.getJSONObject(key)
            } else {
                json.getJSONArray(key)
            }
        } else {
            json as JSONArray
            var index = key as Int
            if (index == -1) {
                index = json.lastIndex
            }

            if (keys[i + 1] is String) {
                json.getJSONObject(index)
            } else {
                json.getJSONArray(index)
            }
        }
    }

    // 读取最后一层
    val key = keys.last()
    // 根据key类型决定当前的json是JSONObject还是JSONArray
    return if (key is String) {
        (json as JSONObject).read(key)
    } else {
        json as JSONArray
        var index = key as Int
        if (index == -1) {
            index = json.lastIndex
        }

        json.read(index)
    }
}

/**
 * 连续读取json结构体
 *
 * @param keys 要读取的每层的key，这里的key可以是[String]或[Int]两种类型。
 * [String]对应读取[JSONObject]里的数据，[Int]对应读取[JSONArray]里的数据，-1表示最后一条数据。
 */
inline fun <reified T> JSONObject.getValue(vararg keys: Any): T {
    if (keys.size == 1) {
        return this.read(keys[0] as String)
    }

    var json: Any = this
    for (i in 0 .. keys.size - 2) {
        val key = keys[i]
        json = if (key is String) {
            json as JSONObject
            if (keys[i + 1] is String) {
                json.getJSONObject(key)
            } else {
                json.getJSONArray(key)
            }
        } else {
            json as JSONArray
            var index = key as Int
            if (index == -1) {
                index = json.lastIndex
            }

            if (keys[i + 1] is String) {
                json.getJSONObject(index)
            } else {
                json.getJSONArray(index)
            }
        }
    }

    val key = keys.last()
    return if (key is String) {
        (json as JSONObject).read(key)
    } else {
        json as JSONArray
        var index = key as Int
        if (index == -1) {
            index = json.lastIndex
        }

        json.read(index)
    }
}

/**
 * 连续读取json结构体
 *
 * @param keys 要读取的每层的key，这里的key可以是[String]或[Int]两种类型。
 * [String]对应读取[JSONObject]里的数据，[Int]对应读取[JSONArray]里的数据，-1表示最后一条数据。
 */
inline fun <reified T> JSONArray.optValue(vararg keys: Any): T? {
    if (keys.size == 1) {
        var index = keys[0] as Int
        if (index == -1) {
            index = this.lastIndex
        }
        return if (index in this.indices) this.read(index) else null
    }

    // 中间层读取
    var json: Any? = this
    for (i in 0 .. keys.size - 2) {
        if (json == null) {
            return null
        }

        val key = keys[i]

        // 根据key类型决定当前的json是JSONObject还是JSONArray
        // 根据下一个key的类型决定读取的值是JSONObject还是JSONArray
        json = if (key is String) {
            json as JSONObject
            if (keys[i + 1] is String) {
                json.optJSONObject(key)
            } else {
                json.optJSONArray(key)
            }
        } else {
            json as JSONArray
            var index = key as Int
            if (index == -1) {
                index = json.lastIndex
            }
            if (index !in json.indices) {
                return null
            }

            if (keys[i + 1] is String) {
                json.optJSONObject(index)
            } else {
                json.optJSONArray(index)
            }
        }
    }
    if (json == null) {
        return null
    }

    // 读取最后一层
    val key = keys.last()
    // 根据key类型决定当前的json是JSONObject还是JSONArray
    return if (key is String) {
        (json as JSONObject).optRead(key)
    } else {
        json as JSONArray
        var index = key as Int
        if (index == -1) {
            index = json.lastIndex
        }
        if (index !in json.indices) {
            return null
        }

        json.read(index)
    }
}

inline fun <reified T> JSONObject.optValue(vararg keys: Any): T? {
    if (keys.size == 1) {
        return this.optRead(keys[0] as String)
    }

    var json: Any? = this
    for (i in 0 .. keys.size - 2) {
        if (json == null) {
            return null
        }

        val key = keys[i]
        json = if (key is String) {
            if (keys[i + 1] is String) {
                (json as JSONObject).optJSONObject(key)
            } else {
                (json as JSONObject).optJSONArray(key)
            }
        } else {
            json as JSONArray
            var index = key as Int
            if (index == -1) {
                index = json.lastIndex
            }
            if (index !in json.indices) {
                return null
            }

            if (keys[i + 1] is String) {
                json.optJSONObject(key)
            } else {
                json.optJSONArray(key)
            }
        }
    }
    if (json == null) {
        return null
    }

    val key = keys.last()
    return if (key is String) {
        (json as JSONObject).optRead(key)
    } else {
        json as JSONArray
        var index = key as Int
        if (index == -1) {
            index = json.lastIndex
        }
        if (index !in json.indices) {
            return null
        }

        json.read(index)
    }
}

inline fun <reified T> JSONArray.read(index: Int): T {
    return when (T::class.java) {
        java.lang.Integer::class.java -> this.getInt(index)
        java.lang.Long::class.java -> this.getLong(index)
        java.lang.Double::class.java -> this.getDouble(index)
        java.lang.Boolean::class.java -> this.getBoolean(index)
        String::class.java -> this.getString(index)
        JSONObject::class.java -> this.getJSONObject(index)
        JSONArray::class.java -> this.getJSONArray(index)
        Any::class.java -> this.get(index)
        else -> throw UnsupportedClassVersionError("${T::class.java}")
    } as T
}

inline fun <reified T> JSONObject.read(key: String): T {
    return when (T::class.java) {
        java.lang.Integer::class.java -> this.getInt(key)
        java.lang.Long::class.java -> this.getLong(key)
        java.lang.Double::class.java -> this.getDouble(key)
        java.lang.Boolean::class.java -> this.getBoolean(key)
        String::class.java -> this.getString(key)
        JSONObject::class.java -> this.getJSONObject(key)
        JSONArray::class.java -> this.getJSONArray(key)
        Any::class.java -> this.get(key)
        else -> throw UnsupportedClassVersionError("${T::class.java}")
    } as T
}

inline fun <reified T> JSONObject.optRead(key: String): T? {
    return when (T::class.java) {
        java.lang.Integer::class.java -> this.optInt(key)
        java.lang.Long::class.java -> this.optLong(key)
        java.lang.Double::class.java -> this.optDouble(key)
        java.lang.Boolean::class.java -> this.optBoolean(key)
        String::class.java -> this.optString(key)
        JSONObject::class.java -> this.optJSONObject(key)
        JSONArray::class.java -> this.optJSONArray(key)
        Any::class.java -> this.opt(key)
        else -> throw UnsupportedClassVersionError("${T::class.java}")
    } as? T?
}